#ifndef CONNECTOR_H
#define CONNECTOR_H

#include "AReVi/arObject.h"
#include "AReVi/scheduler.h"
using namespace AReVi;


class Event;
class FIFO;

/** Abstract class Connector contains default methods 
 *  for both client and server */
class Connector : public ArObject {
public:
	AR_CLASS(Connector)
	
	/** Get my unique ID */
	virtual StlString getID() = 0;
	
	/** Broadcast an event */
    virtual void broadcast(ArRef<Event> evt) = 0;
	
	/** Define callback on any event */
	template <class T1, class T2>
	void addCallback(ArPtr<T1> object, void (T2::*method)(ArRef<Event>));
	/** Define callback on event from special emitter or type */
	template <class T1, class T2>
	void addCallback(StlString field,
	                 int fieldIsEmitter, 
	                 ArPtr<T1> object, void (T2::*method)(ArRef<Event>));
	/** Define callback on special event */
	template <class T1, class T2>
	void addCallback(StlString emitterID,
	                 StlString type,
	                 ArPtr<T1> object, void (T2::*method)(ArRef<Event>));

   /** Callback for you to disconnect the socket properly */
    AR_CALLBACK(Connector, Disconnect, _disconnectCB, int socket;)

protected:
    /** Abstract class -> protected constructor */
	Connector(ArCW & arCW);

    /** Apply special treatment to an Event (for server)
     *  Called before activating callbacks */
    virtual void treatEvent(int socketFrom, ArRef<Event> evt);

    /** Ask Connector to listen on a socket */
    void _listenEventsOnSocket(int socket);
    /** Ask Connector to send an event on a socket */
    void _sendEventOnSocket(int socket, ArRef<Event> evt);

    /** Map[socket] -> stack for receiving */
    StlMap<int, ArRef<FIFO> > _receiveStack;
    /** Map[socket] -> stack for sending */
    StlMap<int, ArRef<FIFO> > _sendStack;

    /** Called when reading / Writing on socket is allowed */
    void _onPolling(const Scheduler::PollingEvent & evt);
    
    /** For AR_CALLBACK */
    CallbackManager<Connector, DisconnectEvent> _disconnectCB;
    
    /** Internal class for callbacks information */
    struct iCallback {
    	bool anyEmitter;
    	StlString emitterID;
    	bool anyType;
    	StlString type;
	    ArPtr<ArObject> object;
	    void (ArObject::*method)(ArRef<Event>);
    };
    
    /** Registered callbacks */
	StlVector<iCallback *> _callbacks;
	
	/** Method to fire all callbacks associated to an event */
	void fireCallbacks(ArRef<Event> event);

};







/** Connector for the server */
class ConnectorServer : public Connector {
public:
	AR_CLASS(ConnectorServer)
	AR_CONSTRUCTOR_1(ConnectorServer, int, port)

	/** Get my unique ID */
	virtual StlString getID() { return "0"; }

	/** Broadcast an event */
    virtual void broadcast(ArRef<Event> evt);

protected:
    /** Apply special treatment to an Event (for server)
     *  Called before activating callbacks */
    virtual void treatEvent(int socketFrom, ArRef<Event> evt);

    /** Socket for listening connexions */
    int _listenSocket;
    /** Clients' sockets */
    StlVector<int> _clientSockets;

    /** To remove dead sockets */
    void _onDisconnect(const Connector::DisconnectEvent & evt);

    /** To react on new connection */
    void _onPolling(const Scheduler::PollingEvent & evt);

    /** Last ID assigned to a client */
    unsigned int _lastID;
};







/** Connector for the client */
class ConnectorClient : public Connector {
public:
	AR_CLASS(ConnectorClient)
	AR_CONSTRUCTOR_2(ConnectorClient, StlString, server, int, port)

	/** Get my unique ID */
	virtual StlString getID() { return _id; }

	/** Broadcast an event */
    virtual void broadcast(ArRef<Event> evt);

protected:
    /** La connection réseau */
    int _socket;
    
    /** My unique ID */
    StlString _id;
};







// INLINE IMPLEMENTATION ### DO NOT CROSS THE LINE ### DO NOT CROSS THE LINE ###
// INLINE IMPLEMENTATION ### DO NOT CROSS THE LINE ### DO NOT CROSS THE LINE ###
// INLINE IMPLEMENTATION ### DO NOT CROSS THE LINE ### DO NOT CROSS THE LINE ###






template <class T1, class T2>
void Connector::addCallback(StlString emitterID,
                            StlString type,
                            ArPtr<T1> object, void (T2::*method)(ArRef<Event>)) {
	iCallback * cb = new iCallback();
	cb->anyEmitter = false;
	cb->anyType = false;
	cb->emitterID = emitterID;
	cb->type = type;
	cb->object = object;
	cb->method = reinterpret_cast<void (ArObject::*)(ArRef<Event> event)>(method);

cerr << "DEBUG: Added callback for [" << ((cb->anyEmitter)? "*" : cb->emitterID) << "->" << ((cb->anyType)? "*" : cb->type) << "]" << endl;
	_callbacks.push_back(cb);
}



template <class T1, class T2>
void Connector::addCallback(StlString field,
                            int fieldIsEmitter, 
                            ArPtr<T1> object, void (T2::*method)(ArRef<Event>)) {
	iCallback * cb = new iCallback();
	cb->anyEmitter = false;
	cb->anyType = false;
	if (fieldIsEmitter) {
		cb->emitterID = field;
		cb->anyType = true;
	}
	else {
		cb->anyEmitter = true;
		cb->type = field;
	}
	cb->object = object;
	cb->method = reinterpret_cast<void (ArObject::*)(ArRef<Event> event)>(method);

cerr << "DEBUG: Added callback for [" << ((cb->anyEmitter)? "*" : cb->emitterID) << "->" << ((cb->anyType)? "*" : cb->type) << "]" << endl;
	_callbacks.push_back(cb);
}



template <class T1, class T2>
void Connector::addCallback(ArPtr<T1> object, void (T2::*method)(ArRef<Event>)) {
	iCallback * cb = new iCallback();
	cb->anyEmitter = true;
	cb->anyType = true;
	cb->object = object;
	cb->method = reinterpret_cast<void (ArObject::*)(ArRef<Event> event)>(method);

cerr << "DEBUG: Added callback for [" << ((cb->anyEmitter)? "*" : cb->emitterID) << "->" << ((cb->anyType)? "*" : cb->type) << "]" << endl;
	_callbacks.push_back(cb);
}

#endif
